﻿using System;
using System.Collections.Generic;

namespace PnFramework
{
    //大家好 我是猫叔 很高兴又见面了 上一次我们简单介绍了一下单例 这一期我们来使用单例构建一个事件系统
    //说到事件系统大家会想到什么呢 举个栗子 在游戏流程中会产生很多很多游戏事件！比如按键输入事件 玩家受击事件 击杀敌人事件
    //在小体量的游戏中 通常会把一些对象操作直接写在脚本中 这会造成对象之间的相互引用 这对后续的扩展和维护产生了非常大的影响
    //所以我们需要一个中介者对象去管理和分发这些事件 也就是我们今天讲到的事件管理器 因为事件管理器可以作为独立模块使用 我们先让他继承单例基类

    /// <summary>
    /// 基于名字的事件系统
    /// </summary>
    public class EventMgr : Singleton<EventMgr>
    {
        //本次用到的知识点 委托 泛型 观察者模式 里氏转换原则
        //首先我们需要一个容器去存储所有事件 并进行区分 没错最好的数据结构是字典 我们声明一个由字符串为 KEY IEvent类为Value的字典结构
        private Dictionary<string, IEvent> mEvents = new Dictionary<string, IEvent>();
        //为什么需要使用一个IEvent接口来作为Value呢？因为我希望事件管理器能够对外提供两种事件形式 即无参数事件和带参数事件 
        //而里氏转换原则能够很好的帮到我们 简单来说就是我们可以用一个父类去派生多个子类 让不同的子类实现不同的功能        
        private interface IEvent { }
        //比如BaseEvent和泛型版本的BaseEvent 分别封装了对应的Action委托
        private class BaseEvent : IEvent { public Action OnEvent; }
        private class BaseEvent<T> : IEvent { public Action<T> OnEvent; }
        //然后我们可以对外提供 两个注册Api 两个注销Api 两个触发Api分别对应无参和有参事件 实现不难 我们先看注册
        /// <summary>
        /// 注册事件
        /// </summary>
        public void Register(string name, Action onEvent)
        {
            //如果当前的事件字典中 存在当前名字的事件对象
            if (mEvents.TryGetValue(name, out IEvent e))
            {
                //就使用as关键字 把父类转化为对应子类 增加注册即可
                if (e is BaseEvent) (e as BaseEvent).OnEvent += onEvent;
                else throw new Exception("注册失败 请使用 Register<T> 进行注册");
            }
            //如果事件字典中不存在当前名字的事件 那就添加一个对象并为其增加一个注册
            else mEvents.Add(name, new BaseEvent() { OnEvent = onEvent });
        }
        /// <summary>
        /// 注册事件
        /// </summary>
        public void Register<T>(string name, Action<T> onEvent)
        {
            //泛型注册方法实现类似
            if (mEvents.TryGetValue(name, out IEvent e))
            {
                if (e is BaseEvent<T>) (e as BaseEvent<T>).OnEvent += onEvent;
                else throw new Exception("注册失败 请使用 Register 进行注册");
            }
            else mEvents.Add(name, new BaseEvent<T>() { OnEvent = onEvent });
        }
        /// <summary>
        /// 注销事件
        /// </summary>
        public void UnRegister(string name, Action onEvent)
        {
            if (!mEvents.TryGetValue(name, out IEvent e)) return;
            //注销事件也是差不多 如果当前的事件字典中 存在当前名字的事件对象 就从当前事件对象移除一个注册
            if (e is BaseEvent) (e as BaseEvent).OnEvent -= onEvent;
            else throw new Exception("注销失败 请使用 UnRegister<T> 进行注销");
        }
        /// <summary>
        /// 注销事件
        /// </summary>
        public void UnRegister<T>(string name, Action<T> onEvent)
        {
            if (!mEvents.TryGetValue(name, out IEvent e)) return;
            if (e is BaseEvent<T>) (e as BaseEvent<T>).OnEvent -= onEvent;
            else throw new Exception("注销失败 请使用 UnRegister 进行注销");
        }
        /// <summary>
        /// 触发事件
        /// </summary>
        public void Trigger(string name)
        {
            if (!mEvents.TryGetValue(name, out IEvent e)) return;
            //触发事件也很像 如果当前的事件字典中 存在当前名字的事件 那就执行当前事件对象中注册的所有事件
            if (e is BaseEvent) (e as BaseEvent).OnEvent?.Invoke();
            else throw new Exception("触发失败 请使用 Trigger<T> 进行触发");
        }
        /// <summary>
        /// 触发事件
        /// </summary>
        public void Trigger<T>(string name, T data)
        {
            //泛型版本的触发事件实现类似 不过多解说
            if (!mEvents.TryGetValue(name, out IEvent e)) return;
            if (e is BaseEvent<T>) (e as BaseEvent<T>).OnEvent?.Invoke(data);
            else throw new Exception("触发失败 请使用 Trigger 进行触发");
        }
    }
}
